/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.google.code.peersim.pastry.controls;

import com.google.code.peersim.pastry.protocol.PastryNode;
import com.google.code.peersim.pastry.protocol.PastryProtocolState;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import peersim.config.Configuration;
import peersim.core.CommonState;
import peersim.core.Control;
import peersim.core.Network;
import peersim.util.FileNameGenerator;

/**
 * This <i>control</i> class logs every nodes' state-tables upon simulation completion.
 *
 * @author frusso
 * @version 0.1
 * @since 0.1
 */
public class PastryStateTablesObserver implements Control {

  /**
   * The name of the log file to log to.
   */
  public static final String LOG = "log";

  private PrintStream stream;
  private String fileName;
  private int uninitialized, initialized, joining, joined = 0;

  /**
   * Constructor.
   *
   * @param prefix
   * @throws java.io.FileNotFoundException
   */
  public PastryStateTablesObserver(String prefix) throws FileNotFoundException {
    fileName = new FileNameGenerator(Configuration.getString(prefix + "."+LOG), ".log").nextCounterName();
    stream = new PrintStream(new FileOutputStream(fileName));
  }

  /**
   * {@inheritDoc}
   */
  public boolean execute() {
    boolean stop = false;
    if(CommonState.getTime() == CommonState.getEndTime()-1) {
      observeStateTables();
    }
    if(CommonState.getTime()==CommonState.getEndTime()) {
      printFinalStats();
    }
    return stop;
  }

  /**
   * Prints to the stream every nodes' state-tables.
   */
  private void observeStateTables() {
    System.err.println("Dumping nodes' state down to "+fileName+", please wait...");
    int n = Network.size();
    stream.println("Network size is: "+n+"\n");
    for(int i=0; i<n; i++) {
      PastryNode node = (PastryNode) Network.get(i);
      stream.println("State-tables for node: "+node.toString()+"\n"+node.getPastryProtocol().toString()+"Local-store: "+node.localStoreAsString()+"\n\n");
      updateCounts(node);
    }
    String info = "Uninitialized: "+uninitialized+" Initialized: "+initialized+" Joining: "+joining+" Joined: "+joined;
    stream.println("\n"+info);
    System.err.println(info+"\n");
  }

  private void printFinalStats() {
    System.err.println(this.getClass().getName()+"\nUninitialized: "+uninitialized+"\nInitialized: "+initialized+"\nJoining: "+joining+"\nJoined: "+joined);
  }

  /**
   * Keeps track of the number of nodes in each possible {@link PastryProtocolState}.
   * 
   * @param node The node whose state we are interested in
   */
  private void updateCounts(PastryNode node) {
    switch(node.getPastryProtocol().getState()) {
      case UNINITIALIZED: {
        uninitialized++;
        break;
      }
      case INITIALIZED: {
        initialized++;
        break;
      }
      case JOINING: {
        joining++;
        break;
      }
      case JOINED: {
        joined++;
        break;
      }
    }
  }
}